package com.nforce.linremote;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Vector;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.ListActivity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.DhcpInfo;
import android.net.wifi.WifiManager;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.NavUtils;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;

import com.google.zxing.integration.android.IntentIntegrator;
import com.google.zxing.integration.android.IntentResult;
import com.nforce.linremote.converters.IpUtils;
import com.nforce.linremote.model.Server;
import com.nforce.linremote.networking.SessionUtils;
import com.nforce.linremote.serverapi.Library;

public class ListHostsActivity extends ListActivity implements
	OnItemClickListener {

    private ConnectTask mConnectTask = null;
    private ScanTask mScanTask = null;
    private ProgressBar mProgressBar;
    private View mStatusView;
    private TextView mStatusMessageView;
    private LayoutInflater mInflater;
    private Vector<Server> data;
    private Server mServer;
    private int mPort;
    private String mIp;
    private String listHostsTask;
    private CustomAdapter mAdapter;
    private SharedPreferences mPrefs;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.activity_list_hosts);
	getActionBar().setDisplayHomeAsUpEnabled(true);

	mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
	mStatusView = findViewById(R.id.host_progress);
	mStatusMessageView = (TextView) findViewById(R.id.actionText);

	mInflater = (LayoutInflater) getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
	data = new Vector<Server>();
	listHostsTask = getIntent().getStringExtra(Constants.EXTRA_LIST_HOSTS);
	mIp = getIntent().getStringExtra(Constants.EXTRA_IP_ADDRESS);

	mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
	String port = mPrefs.getString(Constants.KEY_PREF_PORT,
		Constants.DEFAULT_PORT);
	mPort = Integer.parseInt(port);

	mAdapter = new CustomAdapter(this, R.layout.list_hosts_row,
		R.id.hostView, data);
	setListAdapter(mAdapter);
	getListView().setTextFilterEnabled(true);
	getListView().setOnItemClickListener(this);

	findViewById(R.id.stopButton).setOnClickListener(
		new View.OnClickListener() {
		    @Override
		    public void onClick(View view) {
			cancel();
		    }
		});

	if (Constants.MESSAGE_CONNECT.equals(listHostsTask)) {
	    attemptConnect();
	} else if (Constants.MESSAGE_SCAN.equals(listHostsTask)) {
	    attemptScan();
	}
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent intent) {
	final IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
	if (scanResult != null && scanResult.getContents() != null) {
	    AsyncTask<Void, Void, Void> callJob = new AsyncTask<Void, Void, Void>() {

		@Override
		protected Void doInBackground(Void... params) {
		    SessionUtils.initSession(scanResult.getContents(), mServer);
		    if ((SessionUtils.isSessionActive() && !SessionUtils.isAnotherServerInCache(mServer))) {
			Intent intent = new Intent(getApplicationContext(),
				ProgramListActivity.class);
			startActivity(intent);
		    }
		    return null;
		}
	    };
	    callJob.execute((Void) null);
	}
    }

    private boolean isIpInFavourites(String ip) {
	Set<String> set = mPrefs.getStringSet(Constants.KEY_PREF_FAVS, new HashSet<String>());
	return set.contains(ip);
    }

    private void unstarIp(String ip) {
	Set<String> set = mPrefs.getStringSet(Constants.KEY_PREF_FAVS, new HashSet<String>());
	set.remove(ip);
	mPrefs.edit().putStringSet(Constants.KEY_PREF_FAVS, set).commit();
    }

    private void starIp(String ip) {
	Set<String> set = mPrefs.getStringSet(Constants.KEY_PREF_FAVS, new HashSet<String>());
	set.add(ip);
	mPrefs.edit().putStringSet(Constants.KEY_PREF_FAVS, set).commit();
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, final int pos,
	    long id) {
	cancel();
	mServer = mAdapter.getItem(pos);
	mIp = mServer.getAddress();
	mPort = mServer.getPort();
	AsyncTask<Void, Void, Boolean> callJob = new AsyncTask<Void, Void, Boolean>() {

	    String sid;

	    @Override
	    protected Boolean doInBackground(Void... params) {
		if (!SessionUtils.isSessionActive() || SessionUtils.isAnotherServerInCache(mServer)) {
		    sid = Library.connect(mServer);
		    if (sid != null) {
			SessionUtils.initSession(sid, mServer);
		    }
		} else {
		    return true;
		}
		return sid != null;
	    }

	    @Override
	    protected void onPostExecute(Boolean result) {
		if (result) {
		    Intent intent = new Intent(getApplicationContext(), ProgramListActivity.class);
		    startActivity(intent);
		} else {
		    IntentIntegrator integrator = new IntentIntegrator(ListHostsActivity.this);
		    integrator.addExtra("RESULT_DISPLAY_DURATION_MS", 0L);
		    integrator.initiateScan();
		}
	    }
	};
	callJob.execute((Void) null);
    }

    @Override
    public void onBackPressed() {
	cancel();
	super.onBackPressed();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
	getMenuInflater().inflate(R.menu.simple_menu, menu);
	return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
	switch (item.getItemId()) {
	    case android.R.id.home:
		cancel();
		NavUtils.navigateUpFromSameTask(this);
		return true;
	    case R.id.menu_settings:
		Intent intent = new Intent(this, SettingsActivity.class);
		startActivity(intent);
		return true;
	}
	return super.onOptionsItemSelected(item);
    }

    public void cancel() {
	if (mScanTask != null) {
	    mScanTask.cancel(true);
	    mScanTask = null;
	    showProgress(false);
	}
	if (mConnectTask != null) {
	    mConnectTask.cancel(true);
	    mConnectTask = null;
	    showProgress(false);
	}
    }

    public void attemptScan() {
	if (mScanTask != null) {
	    return;
	}
	mStatusMessageView.setText(R.string.progress_scanning);
	showProgress(true);
	mScanTask = new ScanTask();
	mScanTask.execute((Void) null);
    }

    public void attemptConnect() {
	if (mConnectTask != null) {
	    return;
	}
	mStatusMessageView.setText(R.string.progress_connecting);
	showProgress(true);
	mConnectTask = new ConnectTask();
	mConnectTask.execute((Void) null);
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
    private void showProgress(final boolean show) {
	if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
	    int shortAnimTime = getResources().getInteger(
		    android.R.integer.config_shortAnimTime);

	    mStatusView.setVisibility(View.VISIBLE);
	    mStatusView.animate().setDuration(shortAnimTime)
		    .alpha(show ? 1 : 0)
		    .setListener(new AnimatorListenerAdapter() {
			@Override
			public void onAnimationEnd(Animator animation) {
			    mStatusView.setVisibility(show ? View.VISIBLE
				    : View.GONE);
			}
		    });
	} else {
	    mStatusView.setVisibility(show ? View.VISIBLE : View.GONE);
	}
    }

    private class CustomAdapter extends ArrayAdapter<Server> {

	public CustomAdapter(Context context, int resource,
		int textViewResourceId, List<Server> objects) {
	    super(context, resource, textViewResourceId, objects);

	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
	    ViewHolder holder = null;

	    TextView item = null;
	    TextView description = null;

	    final Server rowData = getItem(position);

	    if (null == convertView) {
		convertView = mInflater.inflate(R.layout.list_hosts_row, null);
		holder = new ViewHolder(convertView);
		convertView.setTag(holder);
	    }

	    Button star = (Button) convertView.findViewById(R.id.star_button);
	    if (isIpInFavourites(rowData.getAddress())) {
		star.setBackgroundResource(R.drawable.ic_action_unstar);
		star.setOnClickListener(new OnClickListener() {

		    @Override
		    public void onClick(View v) {
			unstarIp(rowData.getAddress());
			v.setBackgroundResource(R.drawable.ic_action_star);
		    }
		});
	    } else {
		star.setOnClickListener(new OnClickListener() {

		    @Override
		    public void onClick(View v) {
			starIp(rowData.getAddress());
			v.setBackgroundResource(R.drawable.ic_action_unstar);
		    }
		});
	    }

	    holder = (ViewHolder) convertView.getTag();
	    item = holder.getItem();
	    item.setText(rowData.getHostname());

	    description = holder.getDescription();
	    description.setText(rowData.getAddress());

	    return convertView;
	}
    }

    private class ViewHolder {
	private final View mRow;
	private TextView description = null;
	private TextView item = null;

	public ViewHolder(View row) {
	    mRow = row;
	}

	public TextView getDescription() {
	    if (null == description) {
		description = (TextView) mRow.findViewById(R.id.addressView);
	    }
	    return description;
	}

	public TextView getItem() {
	    if (null == item) {
		item = (TextView) mRow.findViewById(R.id.hostView);
	    }
	    return item;
	}
    }

    private class ConnectTask extends AsyncTask<Void, Void, Boolean> {
	Server server;

	@Override
	protected Boolean doInBackground(Void... params) {
	    server = Library.getServerInfo(mIp, mPort);
	    return server != null;
	}

	@Override
	protected void onPostExecute(final Boolean success) {
	    mConnectTask = null;
	    showProgress(false);

	    if (success) {
		data.add(server);
	    }
	}

	@Override
	protected void onCancelled() {
	    mConnectTask = null;
	    showProgress(false);
	}
    }

    private class ScanTask extends AsyncTask<Void, Server, Boolean> {

	private ExecutorService mPool;
	private int mCount = 0;
	private final byte start = 1;
	private final int size = 253;
	private boolean mIsFinished = false;

	@Override
	protected Boolean doInBackground(Void... arg0) {
	    mProgressBar.setMax(size);
	    mPool = Executors.newFixedThreadPool(20);
	    WifiManager wifiManager = (WifiManager) getApplicationContext()
		    .getSystemService(WIFI_SERVICE);
	    DhcpInfo dhcpInfo = wifiManager.getDhcpInfo();
	    byte ip[] = IpUtils.convertIp(dhcpInfo.ipAddress);
	    ip[3] = start;

	    for (int i = 0; i < size; i++) {
		ip[3]++;
		launch(IpUtils.convertIp(ip), size - 1 == i);
	    }
	    while (!mIsFinished) {
		if (isCancelled()) {
		    break;
		}
	    }

	    if (!data.isEmpty()) {
		return true;
	    }

	    return false;
	}

	@Override
	protected void onPostExecute(final Boolean success) {
	    mScanTask = null;
	    showProgress(false);

	    if (success) {

	    }
	}

	@Override
	protected void onProgressUpdate(Server... values) {
	    if (values[0] != null) {
		data.add(values[0]);
		mAdapter.notifyDataSetChanged();
	    }
	    mCount++;
	    mProgressBar.setProgress(mCount);
	}

	private void launch(int ip, boolean isLast) {
	    if (!mPool.isShutdown()) {
		mPool.execute(new CheckRunnable(IpUtils.convertIpToString(ip),
			isLast));
	    }
	}

	@Override
	protected void onCancelled() {
	    if (mPool != null) {
		synchronized (mPool) {
		    mPool.shutdownNow();
		}
	    }
	    mScanTask = null;
	    showProgress(false);
	    super.onCancelled();
	}

	private class CheckRunnable implements Runnable {
	    private final String addr;
	    private final boolean last;

	    CheckRunnable(String addr, boolean last) {
		this.addr = addr;
		this.last = last;
	    }

	    @Override
	    public void run() {
		if (isCancelled()) {
		    publish(null);
		}
		Server server = Library.getServerInfo(addr, mPort);
		mIsFinished = last;
		publish(server);
	    }
	}

	private void publish(final Server host) {
	    publishProgress(host);
	}
    }
}
